package com.oig.sys.workflow.app.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.oig.common.constants.CommonEnum;
import com.oig.common.util.JsonUtil;
import com.oig.sys.workflow.app.IAppAuditCallOptService;
import com.oig.sys.workflow.constants.ListenerEnums;
import com.oig.sys.workflow.dto.AuditCallMsgDto;
import com.oig.sys.workflow.dto.AutoExeLogDto;
import com.oig.sys.workflow.dto.AutoRuleDto;
import com.oig.sys.workflow.dto.RuleProChangeDto;
import com.oig.sys.workflow.msg.IDepNoticeMsgService;
import com.oig.sys.workflow.msg.IDepTaskService;
import com.oig.sys.workflow.service.ITbAutoExeLogService;
import com.oig.sys.workflow.service.ITbAutoOperationService;
import com.oig.sys.workflow.service.ITbAutoRuleService;
import com.oig.sys.workflow.util.MongoExpCompare;
import com.oig.sys.workflow.vo.AutoOperationVo;
import com.oig.sys.workflow.vo.AutoRuleVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;


@Slf4j
@Component
@RequiredArgsConstructor
public class AppAuditCallOptServiceImpl implements IAppAuditCallOptService {


    private final ITbAutoRuleService autoRuleService;
    private final ITbAutoOperationService autoOperationService;
    private final ITbAutoExeLogService autoExeLogService;
    private final IDepNoticeMsgService depNoticeMsgService;
    private final IDepTaskService depTaskService;



    @Override
    public void dealAuditCall(AuditCallMsgDto auditCallMsgDto) {
        AutoRuleDto autoRuleDto = new AutoRuleDto();
        autoRuleDto.setListenerTableName(auditCallMsgDto.getTableName()); //对象
        autoRuleDto.setListenerEventOperation(auditCallMsgDto.getOperation());//操作
        autoRuleDto.setTenantId(auditCallMsgDto.getTenantId());    //如果异步动作未执行完就退出呢？
        log.debug("autoRuleDto:{}", autoRuleDto);
        //查询出有效的 监听规则
        List<AutoRuleVo> allRule = autoRuleService.findAutoRule(autoRuleDto)  ;
        if (CollectionUtil.isEmpty(allRule)){
            log.debug("null rule");
            return;
        }
        allRule.forEach((item) -> {
            log.debug("规则: {} ,开始判断处理",item.getAutoRuleName());
            String updateFiled = null;
            //当是更新时
            if (CommonEnum.OperationEnum.update.name().equals(item.getListenerEventOperation()) && StrUtil.isNotBlank(item.getListenerUpdateCfg())) {
                RuleProChangeDto ruleProChangeDto = JsonUtil.toObject(item.getListenerUpdateCfg(), RuleProChangeDto.class);
                updateFiled = ruleProChangeDto.getField();
                Map<String, Object> newValue = JsonUtil.stringToCollect(auditCallMsgDto.getNewValue());
                Object nvl = newValue.get(ruleProChangeDto.getField());
                if (ListenerEnums.ProChgListenerType.ANY.getCode().equals(ruleProChangeDto.getListenerType())){
                    Map<String, Object> oldValue = JsonUtil.stringToCollect(auditCallMsgDto.getOldValue());
                    Object ovl = oldValue.get(ruleProChangeDto.getField());
                    if (ObjectUtil.isEmpty(ovl) && ObjectUtil.isEmpty(nvl)){
                        log.debug("原来为空并且现在为空，字段没有变化，跳过");
                        return;
                    }
                } else if (ListenerEnums.ProChgListenerType.TARGET.getCode().equals(ruleProChangeDto.getListenerType())) {
                    log.debug("变更为目标值...field:{}-value:{}",ruleProChangeDto.getField(), nvl);
                    if (nvl==null || !nvl.equals(ruleProChangeDto.getNewValue())) {
                        log.debug("字段不是变为指定的值...");
                        return; //字段不是变为指定的值，不处理了 continue
                    }
                } else if (ListenerEnums.ProChgListenerType.SPECIFIED.getCode().equals(ruleProChangeDto.getListenerType())) {
                    Map<String, Object> oldValue = JsonUtil.stringToCollect(auditCallMsgDto.getOldValue());
                    Object ovl = oldValue.get(ruleProChangeDto.getField());
                    log.debug("指定从a变到b...field:{}-value:{}-ov:{}",ruleProChangeDto.getField(), nvl, ovl);
                    if (nvl==null || ovl==null || !nvl.equals(ruleProChangeDto.getNewValue()) || !ovl.equals(ruleProChangeDto.getOldValue())) {
                        log.debug("字段不是指定的变更...");
                        return; //字段不是指定的变更，不处理  continue
                    }
                } else if (ListenerEnums.ProChgListenerType.STEP.getCode().equals(ruleProChangeDto.getListenerType())) {
                    Map<String, Object> oldValue = JsonUtil.stringToCollect(auditCallMsgDto.getOldValue());
                    Object ovl = oldValue.get(ruleProChangeDto.getField());
                    log.debug("指定字段变动范围大于...{}",ruleProChangeDto.getStepValue());
                    if (!NumberUtil.isNumber(ruleProChangeDto.getStepValue())){
                        log.warn("配置错误，变动步长不是数值");
                        return;
                    }
                    if (!this.checkChangeStep(nvl, ovl, ruleProChangeDto.getStepValue())){
                        log.debug("字段变动范围不超过指定数...");
                        return;
                    }
                } else if (ListenerEnums.ProChgListenerType.NUL_TO_ANY.getCode().equals(ruleProChangeDto.getListenerType())) {
                    Map<String, Object> oldValue = JsonUtil.stringToCollect(auditCallMsgDto.getOldValue());
                    Object ovl = oldValue.get(ruleProChangeDto.getField());
                    log.debug("字段值从无变有...");
                    if (ObjectUtil.isNotEmpty(ovl) || ObjectUtil.isEmpty(nvl)){
                        log.debug("原来不为空或者现在为空.");
                        return;
                    }
                } else if (ListenerEnums.ProChgListenerType.ANY_TO_NUL.getCode().equals(ruleProChangeDto.getListenerType())) {
                    Map<String, Object> oldValue = JsonUtil.stringToCollect(auditCallMsgDto.getOldValue());
                    Object ovl = oldValue.get(ruleProChangeDto.getField());
                    log.debug("字段值从有变无...");
                    if (ObjectUtil.isEmpty(ovl) || ObjectUtil.isNotEmpty(nvl)){
                        log.debug("原来为空或者现在不为空.");
                        return;
                    }
                } else {
                    log.warn("规则变动类型不支持");
                    return;
                }
            }
            log.debug("规则: {} 执行操作",item);
            this.callAutoOperation(item, auditCallMsgDto, updateFiled);
        });
    }


    private boolean checkChangeStep(Object nvl, Object ovl, String step){
        if (ObjectUtil.isEmpty(nvl) || ObjectUtil.isEmpty(ovl)){
            log.debug("old value is null or new value is null");
            return false;
        }
        if (NumberUtil.isNumber(String.valueOf(nvl)) && NumberUtil.isNumber(String.valueOf(ovl))){
            BigDecimal nv = new BigDecimal(String.valueOf(nvl));
            BigDecimal ov = new BigDecimal(String.valueOf(ovl));
            if (nv.subtract(ov).abs().compareTo(new BigDecimal(step)) >= 0) {
                log.debug("字段变化大于指定的步长...");
                return true ; //字段变化大于指定的步长，不处理  continue
            }
        } else {
            try {
                Date nd = Convert.toDate(nvl);
                Date od = Convert.toDate(ovl);
                long changeStep = DateUtil.between(nd, od, DateUnit.DAY);
                log.debug("变动天数：{}", changeStep);
                if (new BigDecimal(changeStep).compareTo(new BigDecimal(step))>=0){
                    log.debug("日期字段变化大于指定的步长...");
                    return true ;
                }
            } catch (Exception e){
                log.warn("not date", e);
            }
        }
        return false;
    }


    /**
     * 读取操作配置，根据不同条件判断，执行不同操作，并记录执行日志
     */
    private void callAutoOperation(AutoRuleVo autoRuleVo, AuditCallMsgDto auditCallMsgDto, String updateFiled){
        log.debug("按条件执行操作");
        List<AutoOperationVo> autoOperationList =  this.autoOperationService.findByRuleId(autoRuleVo.getId()) ;
        autoOperationList.forEach((autoOperation) -> {
            boolean mc = true ;
            String result = "-1";
            try{
                if (StringUtils.isNotBlank(autoOperation.getDataCfg())){
                    mc = MongoExpCompare.compare(autoOperation.getDataCfg(), auditCallMsgDto.getObjJson()) ;
                    log.debug("条件匹配结果：{}", mc);
                }
                if (mc) {
                    //执行操作
                    this.doOperation(autoRuleVo, autoOperation, auditCallMsgDto, updateFiled);
                    result = "1" ;
                }
            } catch (Exception e){
                log.error("do operation err..", e);
                result = "0" ;
            }
            if (!"-1".equals(result)){
                //写执行日志
                this.addExeLog(autoRuleVo, autoOperation, result , auditCallMsgDto);
            }
        });
    }

    /**
     * 执行操作
     */
    private void doOperation(AutoRuleVo autoRuleVo, AutoOperationVo autoOperation, AuditCallMsgDto auditCallMsgDto, String updateFiled) {
        if (CommonEnum.FlagEnum.TRUE_FLAG.getCode().equals(autoOperation.getNotifyFlag())){
            //发通知
            log.debug("notify...");
            this.depNoticeMsgService.addAutoRuleNoticeMsg(autoRuleVo, autoOperation, auditCallMsgDto, updateFiled);
        }
        if (CommonEnum.FlagEnum.TRUE_FLAG.getCode().equals(autoOperation.getTaskFlag())){
            //建任务
            log.debug("assign task...");
            this.depTaskService.addAutoRuleTask(autoRuleVo, autoOperation, auditCallMsgDto);
        }
    }

    private void addExeLog(AutoRuleVo autoRuleVo, AutoOperationVo autoOperation, String result, AuditCallMsgDto auditCallMsgDto){
        AutoExeLogDto exeLogDto = new AutoExeLogDto();
        exeLogDto.setRuleId(autoRuleVo.getId());
        exeLogDto.setOptId(autoOperation.getId());
        exeLogDto.setRelationType(autoRuleVo.getRelationType());                            //业务类型
        JSONObject jsonObject = JSON.parseObject(auditCallMsgDto.getObjJson());
        String relationId = jsonObject.getString(autoRuleVo.getRelationIdColumn()) ;    //业务单号
        exeLogDto.setRelationId(relationId);
        exeLogDto.setExecuteResult("0".equals(result)?"失败":"成功");
        exeLogDto.setExecuteTime(new Date());
        exeLogDto.setRuleJson(JsonUtil.toJSONString(autoRuleVo));
        exeLogDto.setOptJson(autoOperation.getDataCfg());
        exeLogDto.setTenantId(autoRuleVo.getTenantId());
        exeLogDto.setTenantName(autoRuleVo.getTenantName());
        autoExeLogService.addAutoExeLog(exeLogDto) ;
    }


}
